﻿// $Id: Transaction.cs 10 2008-02-29 15:58:02Z mcartoixa $
/*
 *  Copyright (C) 2007 Noury Solutions
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 3 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 */
/*! \file
 * \brief Definition of \c Salamanca.DataAccess.Transaction.
 *
 * \author $Author: mcartoixa $
 * \date $Date: 2008-02-29 16:58:02 +0100 (ven., 29 févr. 2008) $
 * \version $Revision: 10 $
 */

using System;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
#if (!NET_CF)
using System.EnterpriseServices;
using System.Transactions;
#endif

namespace Salamanca.DataAccess
{



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Interface implemented by a transaction.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public interface ITransaction:
        IDisposable
    {
        /// <summary>Commits the transaction.</summary>
        void Commit();

        ///// <summary>Indicates that the transaction has been committed.</summary>
        //event EventHandler Committed;
    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Implementation of a transaction.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public sealed class Transaction:
        ITransaction
    {
        private interface ITransactionAdapter:
            IDisposable
        {
            /// <summary>Commits the transaction.</summary>
            void Commit();

            /// <summary>Rollbacks the transaction.</summary>
            void Rollback();

            object Transaction
            {
                get;
            }
        }

        private abstract class TransactionAdapter<TTransaction>:
            ITransactionAdapter
        {
            /// <summary>Constructor.</summary>
            public TransactionAdapter(TTransaction transaction)
            {
                _Transaction=transaction;
            }

            ~TransactionAdapter()
            {
                Dispose(false);
            }

            /// <summary>Commits the transaction.</summary>
            public abstract void Commit();

            /// <summary>Rollbacks the transaction.</summary>
            public abstract void Rollback();

            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }

            protected virtual void Dispose(bool disposing)
            {
                if (!_Disposed)
                {
                    IDisposable d=_Transaction as IDisposable;
                    if (d!=null)
                        d.Dispose();

                    _Disposed=true;
                }
            }

            protected TTransaction Transaction
            {
                get
                {
                    return _Transaction;
                }
            }

            object ITransactionAdapter.Transaction
            {
                get
                {
                    return Transaction;
                }
            }

            private TTransaction _Transaction;
            private bool _Disposed;
        }

        private class DbConnectionAdapter:
            TransactionAdapter<IDbConnection>
        {
            /// <summary>Constructor.</summary>
            public DbConnectionAdapter(IDbConnection connection):
                base(new Data.DbConnectionAdapter(connection))
            {
                Transaction.Open();
            }

            /// <summary>Commits the transaction.</summary>
            public override void Commit()
            {
                ActualTransaction.Commit();
            }

            /// <summary>Rollbacks the transaction.</summary>
            public override void Rollback()
            {
                ActualTransaction.Rollback();
            }

            protected override void Dispose(bool disposing)
            {
                ActualTransaction.Dispose();

                base.Dispose(disposing);
            }

            public IDbTransaction ActualTransaction
            {
                get
                {
                    if (_ActualTransaction==null)
                        _ActualTransaction=Transaction.BeginTransaction();

                    return _ActualTransaction;
                }
            }

            private IDbTransaction _ActualTransaction;
        }

#if (!NET_CF)

        private class TransactionScopeAdapter:
            TransactionAdapter<TransactionScope>
        {
            /// <summary>Constructor.</summary>
            public TransactionScopeAdapter(TransactionScope transaction):
                base(transaction)
            {
            }

            /// <summary>Commits the transaction.</summary>
            public override void Commit()
            {
                Transaction.Complete();
            }

            /// <summary>Rollbacks the transaction.</summary>
            public override void Rollback()
            {
                // Automatic rollback on disposal
            }

        }

        private abstract class ExplicitTransactionAdapter:
            TransactionAdapter<System.Transactions.Transaction>
        {
            /// <summary>Constructor.</summary>
            public ExplicitTransactionAdapter(System.Transactions.Transaction transaction):
                base(transaction)
            {
            }

            /// <summary>Rollbacks the transaction.</summary>
            public override void Rollback()
            {
                Transaction.Rollback();
            }

        }

        private class CommittableTransactionAdapter:
            ExplicitTransactionAdapter
        {
            /// <summary>Constructor.</summary>
            public CommittableTransactionAdapter(CommittableTransaction transaction):
                base(transaction)
            {
            }

            /// <summary>Commits the transaction.</summary>
            public override void Commit()
            {
                ((CommittableTransaction)Transaction).Commit();
            }
        }

        private class DependentTransactionAdapter:
            ExplicitTransactionAdapter
        {
            /// <summary>Constructor.</summary>
            public DependentTransactionAdapter(DependentTransaction transaction):
                base(transaction)
            {
            }

            /// <summary>Commits the transaction.</summary>
            public override void Commit()
            {
                ((DependentTransaction)Transaction).Complete();
            }
        }

        private class EnterpriseTransactionAdapter:
            TransactionAdapter<System.EnterpriseServices.ITransaction>
        {
            /// <summary>Constructor.</summary>
            public EnterpriseTransactionAdapter(System.EnterpriseServices.ITransaction transaction):
                base(transaction)
            {
            }

            /// <summary>Commits the transaction.</summary>
            public override void Commit()
            {
                Transaction.Commit(0, 1, 0);
            }

            /// <summary>Rollbacks the transaction.</summary>
            public override void Rollback()
            {
                BOID boid=new BOID();
                Transaction.Abort(ref boid, 0, 0);
            }

        }
#endif

#if (!NET_CF)
        /// <summary>Creates a new <see cref="TransactionScope">transaction scope</see>.</summary>
        public Transaction()
#else
        internal Transaction()
#endif
        {
#if (!NET_CF)
            System.Transactions.Transaction current=System.Transactions.Transaction.Current;

            if (current==null)
            {
                _Transaction=new TransactionScopeAdapter(new TransactionScope());
            } else
            {
                _Transaction=new DependentTransactionAdapter(current.DependentClone(DependentCloneOption.RollbackIfNotComplete));
                _RollbackRequired=true;
            }
            //else
            //    // We want to know when the transaction is completed...
            //    current.TransactionCompleted+=new TransactionCompletedEventHandler(
            //        delegate(object sender, TransactionEventArgs e) {
            //            if (e.Transaction.TransactionInformation.Status==System.Transactions.TransactionStatus.Committed)
            //                OnCommitted(new EventArgs());
            //        }
            //    );
#else
            //throw new NotSupportedException(Resources.SR.DistributedTransactionsNotSupported);
#endif
        }

        /// <summary>Creates a new local transaction on the specified <see cref="IDbConnection" />.</summary>
        /// <summary>Creates a new transaction instance, which acts as a wrapper around a local transaction on the specified <see cref="IDbConnection" />.</summary>
        public Transaction(IDbConnection connection)
        {
            _Transaction=new DbConnectionAdapter(connection);
            _RollbackRequired=true;
        }

#if (!NET_CF)
        /// <summary>Unused.</summary>
        private Transaction(System.Transactions.Transaction transaction)
        {
            Debug.Assert(transaction!=null);
            if (transaction==null)
                throw new ArgumentNullException("transaction");

//            _Transaction=new ExplicitTransactionAdapter(transaction);

            // We want to know when the transaction is completed...
            //transaction.TransactionCompleted+=new TransactionCompletedEventHandler(
            //    delegate(object sender, TransactionEventArgs e) {
            //        if (e.Transaction.TransactionInformation.Status==System.Transactions.TransactionStatus.Committed)
            //            OnCommitted(new EventArgs());
            //    }
            //);
        }

        /// <summary>Creates a new transaction instance, which acts as a wrapper around the specified <see cref="CommittableTransaction" />.</summary>
        public Transaction(CommittableTransaction transaction)
        {
            _Transaction=new CommittableTransactionAdapter(transaction);
            _RollbackRequired=true;
        }

        /// <summary>Unused.</summary>
        private Transaction(TransactionScope transaction)
        {
            _Transaction=new TransactionScopeAdapter(transaction);
        }

        /// <summary>Unused.</summary>
        private Transaction(System.EnterpriseServices.ITransaction transaction)
        {
            _Transaction=new EnterpriseTransactionAdapter(transaction);
        }
#endif

        /// <summary>Finalizer.</summary>
        ~Transaction()
        {
            Dispose(false);
        }

        /// <summary>Commits the transaction.</summary>
        public void Commit()
        {
            if (_Transaction!=null)
            {
                _Transaction.Commit();

                OnCommitted(new EventArgs());
            }
        }

        /// <summary>Frees the resources of this instance.</summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        ///// <summary>Raises the <see cref="Committed" /> event.</summary>
        private void OnCommitted(EventArgs e)
        {
            _RollbackRequired=false;

            //if (Committed!=null)
            //    Committed(this, e);
        }

        /// <summary>Frees the resources of this instance.</summary>
        private void Dispose(bool disposing)
        {
            if (!_Disposed)
            {
                if (_RollbackRequired)
                {
                    if (_Transaction!=null)
                        _Transaction.Rollback();
                    _RollbackRequired=false;
                }

                if (_Transaction!=null)
                    _Transaction.Dispose();
                _Disposed=true;
            }
        }

        /// <summary>Attempts to explicitly converts a <see cref="Transaction" /> into a <see cref="DbTransaction" />.</summary>
        public static explicit operator DbTransaction(Transaction t)
        {
            DbConnectionAdapter a=t._Transaction as DbConnectionAdapter;
            if (a!=null)
            {
                IDbTransaction ret=a.ActualTransaction as IDbTransaction;

                Data.DbTransactionAdapter ta=ret as Data.DbTransactionAdapter;
                if (ta!=null)
                    ret=ta.DbTransaction;

                return ret as DbTransaction;
            }

            return null;
        }

#if (!NET_CF)
        /// <summary>Attempts to explicitly converts a <see cref="Transaction" /> into a <see cref="System.Transactions.Transaction" />.</summary>
        public static explicit operator System.Transactions.Transaction(Transaction t)
        {
            if (t._Transaction==null)
                return null;

            System.Transactions.Transaction ret=t._Transaction.Transaction as System.Transactions.Transaction;
            if (ret==null)
            {
                TransactionScope ts=t._Transaction.Transaction as TransactionScope;
                if (ts!=null)
                    ret=System.Transactions.Transaction.Current;
            }

            return ret;
        }
#endif

        private ITransactionAdapter _Transaction;
        private bool _Disposed;
        private bool _RollbackRequired;

        ///// <summary>Indicates that the transaction has been committed.</summary>
        //public event EventHandler Committed;
    }
}
